import { Graph, Shape, Addon, FunctionExt } from "@antv/x6";
import registerNodes from "./nodes/registerNodes";
import { showPorts } from "./utils";
import EventBus from "../../event-bus";
import BusEvents from "../../event-bus/event-types";

const { Dnd } = Addon;

export default class GraphEditor {
  graph = null;
  dnd = null;

  constructor(container, minimapContainer) {
    this.init(container, minimapContainer);
    this.initDnd(); // dnd 用来做拖拽处理的
    this.initRegister();
    this.initEvents();
  }

  /**
   * 初始化 graph
   */
  init(container, minimapContainer) {
    this.container = container;
    const graph = (this.graph = new Graph({
      container: container,
      autoResize: true,
      // 绘制网格
      grid: {
        size: 10,
        visible: true,
        type: "doubleMesh",
        args: [
          {
            color: "#eee", // 主网格线颜色
            thickness: 1, // 主网格线宽度
          },
          {
            color: "#ddd", // 次网格线颜色
            thickness: 1, // 次网格线宽度
            factor: 4, // 主次网格线间隔
          },
        ],
      },
      // 背景
      background: {
        color: "#ffffff",
        // opacity: 0.1,
      },
      // 对齐线
      snapline: true,
      // 滚动画布
      scroller: {
        enabled: true,
        pannable: true,
      },
      // 小地图
      minimap: {
        enabled: true,
        container: minimapContainer,
        width: 160,
        height: 200,
      },
      // 撤销、重做
      history: true,
      // 剪切板
      clipboard: true,
      // 键盘快捷键
      keyboard: {
        enabled: true,
        global: true,
      },
      // 鼠标滚轮缩放
      mousewheel: {
        enabled: true,
        modifiers: ["ctrl"],
      },
      // 点选、框选
      selecting: {
        enabled: true,
        multiple: true,
        rubberband: true,
        modifiers: ["alt"],
        movable: true,
      },
      // 旋转节点
      rotating: false,
      // 缩放节点
      resizing: false,
      // 平移节点
      translating: {
        restrict: false,
      },
      // 平移缩放节点的基础选项
      transforming: {
        clearAll: true,
        clearOnBlankMouseDown: true,
      },
      // 嵌套节点
      embedding: {
        enabled: false,
        // findParent: "bbox",
        // frontOnly: true,
      },
      // 连线选项
      connecting: {
        snap: {
          radius: 50,
        },
        allowBlank: false,
        allowMulti: "withPort",
        allowLoop: false,
        allowNode: false,
        allowEdge: false,
        allowPort: true,
        highlight: true,
        connector: "rounded",
        // connector: 'pai',
        connectionPoint: "boundary",
        router: {
          name: "er",
          args: {
            direction: "V",
            offset: "center",
          },
        },
        createEdge() {
          return new Shape.Edge({
            attrs: {
              line: {
                stroke: "#a0a0a0",
                strokeWidth: 2,
                targetMarker: {
                  name: "classic",
                  size: 7,
                },
              },
            },
          });
        },
        validateEdge: (args) => {
          const { edge } = args;
          return !!(edge && edge.target && edge.target.port);
        },
      },
      // 高亮选项
      highlighting: {
        magnetAvailable: {
          name: "stroke",
          args: {
            padding: 4,
            attrs: {
              "stroke-width": 2,
              stroke: "red",
            },
          },
        },
        magnetAdsorbed: {
          name: "stroke",
          args: {
            attrs: {
              fill: "#fff",
              stroke: "#31d0c6",
            },
          },
        },
      },
    }));
    console.log(graph);
  }

  /**
   * 初始化 dnd
   */
  initDnd() {
    const { graph } = this;
    this.dnd = new Dnd({
      target: graph,
      animation: true,
      validateNode: validate,
      getDropNode: getDropNode,
    });

    function validate() {
      return true;
    }
    function getDropNode(node) {
      let nodeClone = node.clone();
      EventBus.$emit(BusEvents.INTERACT_ADD_NODE, {
        id: nodeClone.id,
        name: nodeClone.getData().name,
      });
      return nodeClone;
    }
  }

  /**
   * 初始化 registerNodes 和 registerEdges
   */
  initRegister() {
    registerNodes();
  }

  /**
   * 初始化 graph 相关的事件
   */
  initEvents() {
    const { graph } = this;
    const container = graph.container;

    /** 将 command 和 key 进行绑定 */
    this.bindKeys(graph);
    // 节点选择 selection:changed
    graph.on("selection:changed", (args) => {
      const { removed, selected } = args;

      // todo: 将所有的 selected 节点告知主界面，以方便 props-panel 使用

      selected.forEach((cell) => {
        if (cell.isNode()) {
          cell.data.selected = true;
        }
      });
      removed.forEach((cell) => {
        if (cell.isNode()) {
          cell.data.selected = false;
        }
      });
    });
    /** 节点相关的事件 */
    graph.on(
      "node:mouseenter",
      FunctionExt.debounce(() => {
        const ports = container.querySelectorAll(".x6-port-body");
        const texts = container.querySelectorAll(".x6-port-label");
        showPorts(ports, true);
        showPorts(texts, true);
      })
    );
    graph.on(
      "node:mouseleave",
      FunctionExt.debounce(() => {
        const ports = container.querySelectorAll(".x6-port-body");
        const texts = container.querySelectorAll(".x6-port-label");
        showPorts(ports, false);
        showPorts(texts, false);
      })
    );
    graph.on("node:dblclick", (node) => {
      EventBus.$emit(BusEvents.INTERACT_DOUBLE_CLICK_NODE, { node: node.node });
    });
  }

  /**
   * 对 graph 绑定一些 Key 事件
   */
  bindKeys(graph) {
    graph.bindKey("ctrl+z", () => {
      EventBus.$emit(BusEvents.COMMAND_UNDO);
    });
    graph.bindKey("ctrl+shift+z", () => {
      EventBus.$emit(BusEvents.COMMAND_REDO);
    });
    graph.bindKey("ctrl+c", () => {
      EventBus.$emit(BusEvents.COMMAND_COPY);
    });
    graph.bindKey("ctrl+v", () => {
      EventBus.$emit(BusEvents.COMMAND_PASTE);
    });
    graph.bindKey("ctrl+x", () => {
      EventBus.$emit(BusEvents.COMMAND_CUT);
    });
    graph.bindKey("delete", () => {
      EventBus.$emit(BusEvents.COMMAND_DELETE);
    });
    graph.bindKey("ctrl+s", () => {
      EventBus.$emit(BusEvents.COMMAND_SAVE);
    });
    graph.bindKey("esc", () => {
      EventBus.$emit(BusEvents.COMMAND_CHANGE_MODE);
    });
  }
}
